const { validate: uuidValidate, v4: uuidv4 } = require('uuid')
const cloneDeep = require('lodash/cloneDeep')
const compact = require('lodash/compact')

const changeCoord = (location, _x, _y, _f, minX, minY) => {
  return [(location[0] - minX) * _f + _x + minX, (location[1] - minY) * _f + _y + minY, location[2]]
}

function fusion(architecture, structure) {
  const allX4A = []
  const allY4A = []
  const allX4S = []
  const allY4S = []
  const architectureLayerMainWallId = architecture.cols.layer.find(i => i.type === 'MainWall').id
  const structureLayerMainWallId = structure.cols.layer.find(i => i.type === 'MainWall').id
  const architectureLayerBeamId = architecture.cols.layer.find(i => i.type === 'Beam').id
  const structureLayerBeamId = structure.cols.layer.find(i => i.type === 'Beam').id
  const architectureLayerColumnId = architecture.cols.layer.find(i => i.type === 'Column').id
  const structureLayerColumnId = structure.cols.layer.find(i => i.type === 'Column').id

  architecture.cols.line
    .filter(i => i.layer === architectureLayerMainWallId)
    .forEach(i => {
      if (i.start && i.end) {
        const start = architecture.docs[i.start]
        const end = architecture.docs[i.end]
        allX4A.push(start.location[0])
        allY4A.push(start.location[1])
        allX4A.push(end.location[0])
        allY4A.push(end.location[1])
      }
    })
  structure.cols.line
    .filter(i => i.layer === structureLayerMainWallId)
    .forEach(i => {
      if (i.start && i.end) {
        const start = structure.docs[i.start]
        const end = structure.docs[i.end]
        allX4S.push(start.location[0])
        allY4S.push(start.location[1])
        allX4S.push(end.location[0])
        allY4S.push(end.location[1])
      }
    })
  const boundingBoxA = [
    [Math.min(...allX4A), Math.min(...allY4A)],
    [Math.max(...allX4A), Math.max(...allY4A)]
  ]
  const boundingBoxS = [
    [Math.min(...allX4S), Math.min(...allY4S)],
    [Math.max(...allX4S), Math.max(...allY4S)]
  ]
  const _f = (boundingBoxA[1][1] - boundingBoxA[0][1]) / (boundingBoxS[1][1] - boundingBoxS[0][1])
  const _x = Math.min(...allX4A) - Math.min(...allX4S)
  const _y = Math.min(...allY4A) - Math.min(...allY4S)
  const minX = boundingBoxS[0][0]
  const minY = boundingBoxS[0][1]
  structure.cols.line.forEach(line => {
    if (line.layer === structureLayerBeamId || line.layer === structureLayerColumnId) {
      if (line.layer === structureLayerBeamId) line.layer = architectureLayerBeamId
      if (line.layer === structureLayerColumnId) line.layer = architectureLayerColumnId
      architecture.docs[line.id] = line
      const startPoint = {
        ...structure.docs[line.start],
        location: changeCoord(structure.docs[line.start].location, _x, _y, _f, minX, minY)
      }
      const endPoint = {
        ...structure.docs[line.end],
        location: changeCoord(structure.docs[line.end].location, _x, _y, _f, minX, minY)
      }
      architecture.docs[startPoint.id] = startPoint
      architecture.docs[endPoint.id] = endPoint
      architecture.docs[line.material] = structure.docs[line.material]
    }
  })
  architecture.cols = {}
  const structureBoundingbox = structure.cols.boundingbox[0]
  architecture.docs[structureBoundingbox.id] = structureBoundingbox
  const change = {
    id: uuidv4(),
    x: _x,
    y: _y,
    f: _f,
    minX,
    minY,
    definition: 'change'
  }
  architecture.docs[change.id] = change
  for (const key in architecture.docs) {
    const node = architecture.docs[key]
    const definition = node.definition
    if (definition) {
      if (!architecture.cols[definition]) architecture.cols[definition] = []
      architecture.cols[definition].push(node)
    }
  }
  return architecture
}

function getTypeOfVariable(variable) {
  const str = Object.prototype.toString.call(variable)
  return str.substring(8, str.length - 1)
}

function getBoundingBox(data, type) {
  const xArr = []
  const yArr = []
  const pointForBbox = []
  if (type === 'structure') {
    const layerOfMainWallId = data.cols.layer.find(i => i.type === 'MainWall').id
    const layerOfBeamId = data.cols.layer.find(i => i.type === 'Beam').id
    const layerOfColumnId = data.cols.layer.find(i => i.type === 'Column').id
    data.cols.line.forEach(i => {
      if (i.layer === layerOfMainWallId || i.layer === layerOfBeamId || i.layer === layerOfColumnId) {
        pointForBbox.push(data.docs[i.start])
        pointForBbox.push(data.docs[i.end])
      }
    })
    pointForBbox.forEach(i => {
      xArr.push(i.location[0])
      yArr.push(i.location[1])
    })
    const maxX = Math.max(...xArr)
    const maxY = Math.max(...yArr)
    const minX = Math.min(...xArr)
    const minY = Math.min(...yArr)
    return {
      id: uuidv4(),
      maxX,
      maxY,
      minX,
      minY,
      type,
      StructureDottedLine: true,
      definition: 'boundingbox'
    }
  } else {
    const layerOfMainWallId = data.cols.layer.find(i => i.type === 'MainWall').id
    const layerOfBeamId = data.cols.layer.find(i => i.type === 'DryWall').id
    const layerOfWindowId = data.cols.layer.find(i => i.type === 'Window').id
    const layerOfDoorId = data.cols.layer.find(i => i.type === 'Door').id
    data.cols.line.forEach(i => {
      if (
        i.layer === layerOfMainWallId ||
        i.layer === layerOfBeamId ||
        i.layer === layerOfWindowId ||
        i.layer === layerOfDoorId
      ) {
        pointForBbox.push(data.docs[i.start])
        pointForBbox.push(data.docs[i.end])
      }
    })
    pointForBbox.forEach(i => {
      xArr.push(i.location[0])
      yArr.push(i.location[1])
    })
    const maxX = Math.max(...xArr)
    const maxY = Math.max(...yArr)
    const minX = Math.min(...xArr)
    const minY = Math.min(...yArr)
    return {
      id: uuidv4(),
      maxX,
      maxY,
      minX,
      minY,
      type,
      StructureDottedLine: true,
      definition: 'boundingbox'
    }
  }
}

function handleData({ architecture = {}, structure = {} }) {
  if (!architecture.elements) architecture = null
  if (!structure.elements) structure = null
  if (!architecture && !structure) return {}

  if (architecture) architecture.boundingbox = []
  if (structure) structure.boundingbox = []

  const cols = {}
  const docs = {}
  const blocks = []

  console.log('加载architecture')
  let nodes
  if (architecture) {
    nodes = architecture.elements.nodes
  } else if (structure) {
    nodes = structure.elements.nodes
  }
  const allLineStart = {}
  const allLineEnd = {}
  for (let i = 0; i < nodes.length; i++) {
    let node
    if (nodes[i].data) {
      node = { ...nodes[i].data, definition: nodes[i].data.definition }
    } else {
      node = { ...nodes[i], definition: nodes[i].definition }
    }
    for (const key in node) {
      if (getTypeOfVariable(node[key]) === 'Object' && Object.keys(node[key]).length === 1 && node[key].id) {
        node[key] = node[key].id
      }
    }
    if (
      (node.definition === 'MainWall' ||
        node.definition === 'DryWall' ||
        node.definition === 'Door' ||
        node.definition === 'Window' ||
        node.definition === 'Beam' ||
        node.definition === 'Column' ||
        node.definition === 'Floor' ||
        node.definition === 'Ceiling') &&
      node.component
    ) {
      node.type = node.definition
      node.name = node.definition
      node.definition = 'Block'
      blocks.push(node)
    }
    node.definition = node.definition.toLowerCase()
    const definition = node.definition
    if (definition === 'layer') {
      if (!node.type || uuidValidate(node.type)) {
        node.type = node.name
      }
    }

    if (node.definition === 'line') {
      if (
        !(
          (allLineStart[node.start] &&
            node.end === allLineStart[node.start].end &&
            node.layer === allLineStart[node.start].layer) ||
          (allLineEnd[node.end] &&
            node.start === allLineEnd[node.end].start &&
            node.layer === allLineEnd[node.end].layer) ||
          node.start === node.end
        )
      ) {
        allLineStart[node.start] = { end: node.end, layer: node.layer }
        allLineEnd[node.end] = { start: node.end, layer: node.layer }
        docs[node.id] = node
      }
    } else {
      docs[node.id] = node
    }
  }
  for (let i = 0; i < blocks.length; i++) {
    blocks[i].component.forEach(id => {
      if (docs[id]) docs[id].block = blocks[i].id
    })
  }
  for (const key in docs) {
    const node = docs[key]
    const definition = node.definition
    if (definition) {
      if (!cols[definition]) cols[definition] = []
      cols[definition].push(node)
    }
  }

  let type = null
  if (architecture) {
    type = 'architecture'
  } else if (structure) {
    type = 'structure'
  }
  if (type) {
    const boundingbox = getBoundingBox({ cols, docs }, type)
    docs[boundingbox.id] = boundingbox
    if (!cols.boundingbox) cols.boundingbox = []
    cols.boundingbox.push(boundingbox)
  }
  if (architecture) {
    const architectureData = { cols, docs }
    const structureData = handleData({ structure: architecture })
    return fusion(architectureData, structureData)
  } else if (!architecture && structure) {
    return { cols, docs }
  } else if (architecture && structure) {
    const architectureData = { cols, docs }
    const structureData = handleData({ structure })
    return fusion(architectureData, structureData)
  }
}

const getRicent = (data = {}, bim = {}) => {
  data = addTitle('data', { data, bim })
  bim = addTitle('bim', { data, bim })
  const newBIM = cloneDeep(bim)
  if (!newBIM.cols) return {}
  newBIM.cols.auto_plan_point = cloneDeep(newBIM.cols.scanstation)
  for (let i = 0; i < newBIM.cols.auto_plan_point.length; i++) {
    newBIM.cols.auto_plan_point[i] = {
      ...newBIM.cols.auto_plan_point[i],
      definition: 'auto_plan_point'
    }
  }

  const newMainWall = cloneDeep(newBIM.cols.mainwall)
  newMainWall.forEach((i, index) => {
    i.lines.forEach(line => {
      if (!line[0]) {
        newBIM.cols.mainwall[index] = null
      }
    })
  })

  const newDryWall = cloneDeep(newBIM.cols.drywall)
  newDryWall.forEach((i, index) => {
    i.lines.forEach(line => {
      if (!line[0]) {
        newBIM.cols.drywall[index] = null
      }
    })
  })

  newBIM.cols.mainwall = compact(newBIM.cols.mainwall)
  newBIM.cols.drywall = compact(newBIM.cols.drywall)
  newBIM.cols.room = newBIM.cols.room.map(i => {
    return {
      ...i,
      template: i.apartment
    }
  })

  const ricent = {}

  ricent.template = []
  ricent.room = newBIM.cols.room
  ricent.mainwall = []
  ricent.drywall = []
  ricent.opening = []

  newBIM.cols.apartment.forEach(i => {
    const geojson = {
      type: 'Feature',
      properties: {},
      geometry: {
        type: 'MultiPolygon',
        coordinates: []
      }
    }
    i.rooms = []
    ricent.room.forEach(o => {
      if (o.apartment === i.id && o.points && o.points.length > 0) {
        geojson.geometry.coordinates.push([o.points])
        i.rooms.push(o.name)
      }
    })
    i.geojson = geojson
    ricent.template.push({ ...i })
  })
  newBIM.cols.opening.forEach(i => {
    const lines = []
    i.data.forEach(o => lines.push([data.docs[o].location[0], data.docs[o].location[1]]))
    ricent.opening.push({ ...i, lines, data: undefined })
  })
  newBIM.cols.mainwall.forEach(i => {
    const lines = []
    i.lines.forEach(o => {
      if (o[0]) {
        const line = [
          [data.docs[o[0]].location[0], data.docs[o[0]].location[1]],
          [data.docs[o[1]].location[0], data.docs[o[1]].location[1]]
        ]
        lines.push(line)
      }
    })
    ricent.mainwall.push({ ...i, lines, data: undefined })
  })

  newBIM.cols.drywall.forEach(i => {
    const lines = []
    i.lines.forEach(o => {
      if (o[0]) {
        const line = [
          [data.docs[o[0]].location[0], data.docs[o[0]].location[1]],
          [data.docs[o[1]].location[0], data.docs[o[1]].location[1]]
        ]
        lines.push(line)
      }
    })
    ricent.drywall.push({ ...i, lines, data: undefined })
  })

  newBIM.cols.door = newBIM.cols.door.filter(i => i.room)
  newBIM.cols.window = newBIM.cols.window.filter(i => i.room)

  ricent.auto_plan_point = [...newBIM.cols.auto_plan_point]

  // ricent.auto_plan_point = newBIM.cols.auto_plan_point.map(i => {
  //   const newPoint = { ...i }
  //   if (!data.cols.point.find(p => p.name === 'StructureDottedLine')) {
  //     if (i.stage.includes('concrete')) {
  //       newPoint.point = changeS2ABybbox(newPoint.point, data.cols.boundingbox, data.cols.change)
  //     }
  //   }
  //   return newPoint
  // })

  const boundingBoxForArchitecture = data.cols.boundingbox.find(i => i.type === 'architecture')
  const boundingBoxForStructure = data.cols.boundingbox.find(i => i.type === 'structure')

  const handleBbox2Square = ({ maxX, maxY, minX, minY }) => {
    const w = maxX - minX
    const h = maxY - minY
    const diff = Math.abs(w - h) / 2
    if (w >= h) {
      minY = minY - diff
      maxY = maxY + diff
    } else if (w < h) {
      minX = minX - diff
      maxX = maxX + diff
    }
    return [maxX, maxY, minX, minY]
  }

  const [dxfMaxX, dxfMaxY, dxfMinX, dxfMinY] = handleBbox2Square(boundingBoxForArchitecture || boundingBoxForStructure)

  ricent.boundingBox = {
    dxfMaxX,
    dxfMaxY,
    dxfMinX,
    dxfMinY
  }
  const ricentData = { ...data.cols, ricent, ...newBIM.cols }
  return ricentData
}

// const changeS2ABybbox = ([x, y], boundingbox, change) => {
//   //boundingbox, change都是list
//   //handleBbox2Square将bbox扩充为正方形
//   if (change.length === 0) return [x, y]
//   const handleBbox2Square = boundingBox => {
//     if (!boundingBox) return [null, null, null, null]
//     let { maxX, maxY, minX, minY } = boundingBox
//     const w = maxX - minX
//     const h = maxY - minY
//     const diff = Math.abs(w - h) / 2
//     if (w >= h) {
//       minY = minY - diff
//       maxY = maxY + diff
//     } else if (w < h) {
//       minX = minX - diff
//       maxX = maxX + diff
//     }
//     return [maxX, maxY, minX, minY]
//   }

//   const boundingBoxForArchitecture = boundingbox.find(i => i.type === 'architecture')
//   const boundingBoxForStructure = boundingbox.find(i => i.type === 'structure')
//   if (boundingBoxForArchitecture.StructureDottedLine || boundingBoxForStructure.StructureDottedLine) {
//     return [x, y]
//   }
//   // eslint-disable-next-line no-unused-vars
//   const [AmaxX, AmaxY, AminX, AminY] = handleBbox2Square(boundingBoxForArchitecture)

//   // eslint-disable-next-line no-unused-vars
//   const [SmaxX, SmaxY, SminX, SminY] = handleBbox2Square(boundingBoxForStructure)

//   const f4bbox = (AmaxX - AminX) / (SmaxX - SminX)

//   change = change[0]

//   const [xA2S, yA2S] = [
//     (x - change.minX - change.x) / change.f + change.minX,
//     (y - change.minY - change.y) / change.f + change.minY
//   ]
//   const [xS2AbyBbox, yS2AbyBbox] = [(xA2S - SminX) * f4bbox + AminX, (yA2S - SminY) * f4bbox + AminY]
//   return [xS2AbyBbox, yS2AbyBbox]
// }

const capital = key => {
  const noun = {
    stage: 'Stage',
    line: 'Line',
    layer: 'Layer',
    point: 'Point',
    block: 'Block',
    arc: 'Arc',
    ellipse: 'Ellipse',
    text: 'Text',
    polyline: 'Polyline',
    wall: 'Wall',
    opening: 'Opening',
    entity: 'Entity',
    mainwall: 'MainWall',
    solid: 'Solid',
    drywall: 'DryWall',
    door: 'Door',
    window: 'Window',
    circle: 'Circle',
    beam: 'Beam',
    column: 'Column',
    room: 'Room',
    apartment: 'Apartment', //template
    scanstation: 'Scanstation', //scanstation
    building: 'Building',
    ceiling: 'Ceiling',
    floor: 'Floor',
    concrete: 'Concrete',
    masonry: 'Masonry',
    plaster: 'Plaster',
    separator: 'Separator',
    displaytext: 'DisplayText'
  }
  if (noun[key]) {
    return noun[key]
  } else {
    return key
  }
}

const isBIM = key => {
  if (
    key === 'apartment' ||
    key === 'room' ||
    key === 'door' ||
    key === 'window' ||
    key === 'opening' ||
    key === 'scanstation' ||
    key === 'auto_plan_task' ||
    key === 'bim' ||
    key === 'mainwall' ||
    key === 'drywall' ||
    key === 'beam' ||
    key === 'wall' ||
    key === 'column' ||
    key === 'separator'
  ) {
    return true
  } else {
    return false
  }
}

const addTitle = (type, obj) => {
  const title = [
    'timestamp',
    'point',
    'line',
    'rendermaterial',
    'layer',
    'block',
    'polyline',
    'solid',
    'arc',
    'ellipse',
    'circle',
    'text',
    'opening',
    'window',
    'door',
    'room',
    'wall',
    'drywall',
    'mainwall',
    'beam',
    'column',
    'scanstation',
    'auto_plan_task',
    'apartment',
    'polylinechildren',
    'bim',
    'entity',
    'boundingbox',
    'change',
    'separator',
    'displaytext'
  ]
  if (type === 'data') {
    let data = { cols: {}, docs: {} }
    if (obj && obj.data) data = obj.data
    title.forEach(i => {
      if (!isBIM(i) && !data.cols[i]) data.cols[i] = []
    })
    return data
  } else if (type === 'elements') {
    let elements = { nodes: [] }
    if (obj && obj.elements) elements = obj.elements
    return elements
  } else {
    let bim = { cols: {}, docs: {} }
    if (obj && obj.bim) bim = obj.bim
    title.forEach(i => {
      if (isBIM(i) && !bim.cols[i]) bim.cols[i] = []
    })
    return bim
  }
}

const addData = _data => {
  const data = addTitle('data')
  const bim = addTitle('bim')
  if (!_data.cols.entity || _data.cols.entity.length === 0) {
    _data.cols.entity = [{ name: 'entity01', id: uuidv4(), definition: 'entity' }]
  }
  if (!_data.cols.bim || _data.cols.bim.length === 0) {
    _data.cols.bim = [{ name: 'bim01', id: uuidv4(), definition: 'bim' }]
  }
  for (const key in _data.cols) {
    if (isBIM(key)) {
      bim.cols[key] = _data.cols[key]
    } else {
      data.cols[key] = _data.cols[key]
    }
  }
  for (const key in data.cols) {
    data.cols[key].forEach(doc => {
      data.docs[doc.id] = doc
    })
  }
  for (const key in bim.cols) {
    bim.cols[key].forEach(doc => {
      bim.docs[doc.id] = doc
    })
  }
  return { data, bim }
}

function getUGM({ architecture, data, bim }) {
  if (architecture) {
    const obj = addData(handleData({ architecture }))
    data = obj.data
    bim = obj.bim
  } else {
    data = cloneDeep(data)
    bim = cloneDeep(bim)
  }
  if (!data.docs) return null
  if (!data.cols.point.length) return null
  const nodes = Object.values(data.docs).map(node => {
    return { ...node, definition: capital(node.definition) }
  })

  if (!data.cols.timestamp || !data.cols.timestamp.length) {
    const timestamp = { id: uuidv4(), timestamp: Date.now().toString(), definition: 'timestamp' }
    data.cols.timestamp = [timestamp]
    data.docs[timestamp.id] = timestamp
  }

  const ricentJson = data.cols.point.length > 0 ? getRicent(data, bim) : {}
  const ugm = {
    directed: false,
    multigraph: false,
    elements: { nodes: nodes },
    data: bim ? data : {},
    ...ricentJson
  }
  ugm.bim = bim
  return ugm
}

const translate = name => {
  const noun = {
    圆: 'circle',
    固体: 'solid',
    填充: 'filled',
    线: 'line',
    图层: 'layer',
    构件: 'block',
    曲线: 'curve',
    圆弧: 'arc',
    椭圆弧: 'ellipse',
    文字: 'text',
    折线: 'polyline',
    墙: 'wall',
    空洞: 'opening',
    实体: 'entity',
    图元: 'primitive',
    承重墙: 'mainwall',
    隔墙: 'drywall',
    门: 'door',
    窗: 'window',
    梁: 'beam',
    柱: 'column',
    房间: 'room',
    户型: 'apartment', //template
    扫描站点: 'scanstation',
    天花板: 'ceiling',
    地板: 'floor',
    阶段: 'stage',
    混凝土: 'concrete',
    砌筑: 'masonry',
    抹灰: 'plaster',
    结构: 'structure',
    建筑: 'architecture',
    项目: 'project',
    分期: 'installment',
    楼栋: 'building',
    楼层: 'storey',
    分割线: 'separator',
    显示文字: 'displaytext'
  }
  if (noun[name]) {
    return noun[name]
  } else {
    for (const key in noun) {
      if (noun[key] === name || noun[key] === name.toLowerCase()) {
        return key
      }
    }
  }
  return name
}

// const getRoomNames = () => {
//   const names = JSON.parse(fs.readFileSync('./roomNames.json', 'utf8'))
//   console.log(names)
//   return names
// }

module.exports = { getUGM, capital, addTitle, translate }
